package adc

import (
	"fmt"
	"go.uber.org/zap"
	"strconv"
	"sync"
	"wzgames/internal/admin/model"
	"wzgames/internal/codec/sfs"
	"wzgames/internal/constant"
	"wzgames/internal/response"
	"wzgames/pkg/actor/actor"
	"wzgames/pkg/actor/actorservice"
	"wzgames/pkg/logger"
	"wzgames/pkg/server/ws"
	"wzgames/pkg/utils/dateutil"
)

type Admin struct {
	pid  *actor.PID
	meta *model.Admin
	mu   sync.RWMutex

	token       string
	clientToken string
	wsSess      ws.ISession

	activeTime int64
}

func NewAdmin(u *model.Admin) *Admin {
	adm := &Admin{
		meta:       u,
		activeTime: dateutil.UnixMilli(),
	}

	name := fmt.Sprintf("admin-%s", strconv.FormatInt(u.AdminID, 10))
	props := actor.PropsFromProducer(func() actor.Actor { return adm })
	pid, err := actorservice.ActorSystem().Root.SpawnNamed(props, name)
	if err != nil {
		logger.Error("[Admin] spawn admin actor error", zap.Int64("AdminId", u.AdminID), zap.Error(err))
		adm = nil
		return nil
	}
	adm.pid = pid

	return adm
}

func (adm *Admin) AdminId() int64 {
	return adm.meta.AdminID
}

func (adm *Admin) Pid() *actor.PID {
	return adm.pid
}

func (adm *Admin) Name() string {
	return adm.meta.AdminName
}

func (adm *Admin) NickName() string {
	return adm.meta.NickName
}

func (adm *Admin) Avatar() string {
	return adm.meta.Avatar
}

func (adm *Admin) Receive(ctx actor.Context) {
	switch m := ctx.Message().(type) {
	case *ws.MsgEvent:
		adm.handleExControl(m)
	case constant.ActiveCheck:
		adm.ActiveCheck()
	case constant.WSClosed:
		adm.handleWsClose(m)
	}
}

func (adm *Admin) UpdateMeta(a *model.Admin) {
	adm.mu.Lock()
	defer adm.mu.Unlock()
	adm.meta = a
}

func (adm *Admin) Login(token, clientToken string) {
	adm.mu.Lock()
	defer adm.mu.Unlock()
	if adm.token == token && adm.clientToken == clientToken {
		return
	}
	adm.CloseWS()
	adm.token = token
	adm.clientToken = clientToken
}

func (adm *Admin) CloseWS() {
	if adm.wsSess != nil {
		adm.wsSess.Close()
	}
}

func (adm *Admin) Logout() {
	adm.mu.Lock()
	defer adm.mu.Unlock()
	PP().Logout(adm.AdminId(), adm.token)
	adm.token = ""
	adm.clientToken = ""
	adm.CloseWS()
}

func (adm *Admin) WsLogin(sess ws.ISession, token, clientToken string) bool {
	adm.mu.Lock()
	defer adm.mu.Unlock()
	if adm.token != token || adm.clientToken != clientToken {
		return false
	}
	adm.CloseWS()
	adm.wsSess = sess
	sess.SetLink(adm.Pid(), adm.AdminId())
	return true
}

func (adm *Admin) handleWsClose(data constant.WSClosed) {
	defer logger.Recover()
	if data.Handle > 0 {

	}
}

func (adm *Admin) SendMessage(msg interface{}) {
	if adm.wsSess != nil {
		adm.wsSess.Send(msg)
	}
}

func (adm *Admin) handleExControl(data *ws.MsgEvent) {
	defer logger.Recover()

	//instId := data.Session().LinkID()
	if receiver.msgHandler != nil {
		receiver.msgHandler(adm, data.Message())
	}
	adm.Active()
}

func (adm *Admin) ErrMsg(code int16, cmd, msg string) {
	m := response.AcquireMsgO13()
	xw := sfs.XWriter{}
	defer xw.Reset()
	xw.WriteUShort(uint16(2))
	xw.WriteStringS(sfs.CmdKey, cmd)
	xw.WriteSfsObjectPre(sfs.ParamId, 2)
	xw.WriteShortS(sfs.KeyCode, code)
	xw.WriteStringS(sfs.KeyErrorMessage, msg)
	xw.WriteTo(m)

	adm.SendMessage(m)
}

func (adm *Admin) Active() {
	adm.activeTime = dateutil.UnixSecs()
}

func (adm *Admin) ActiveCheck() {
	if adm.activeTime+dateutil.Hour < dateutil.UnixSecs() {
		adm.stop()
	}
}

func (adm *Admin) stop() {
	adm.mu.Lock()
	defer adm.mu.Unlock()
	GetReceiver().CloseAdmin(adm.AdminId())

	actorservice.Stop(adm.pid)
}
